﻿using System;
using System.Collections.Generic;
using System.Threading;
using Grace.Data.Immutable;
using Grace.DependencyInjection.Impl.CompiledStrategies;
using Grace.DependencyInjection.Lifestyle;

namespace Grace.DependencyInjection.Impl.InstanceStrategies
{
  /// <summary>Base export strategy for all instance and factory exports</summary>
  public abstract class BaseInstanceExportStrategy : ConfigurableActivationStrategy, ICompiledExportStrategy, IInstanceActivationStrategy
  {
    private ImmutableLinkedList<ICompiledExportStrategy> _secondaryStrategies = ImmutableLinkedList<ICompiledExportStrategy>.Empty;

    /// <summary>delegate for the strategy</summary>
    protected ActivationStrategyDelegate StrategyDelegate;

    /// <summary>Default constructor</summary>
    /// <param name="activationType"></param>
    /// <param name="injectionScope"></param>
    protected BaseInstanceExportStrategy(Type activationType, IInjectionScope injectionScope)
      : base(activationType, injectionScope) { }

    /// <summary>Get an activation expression for this strategy</summary>
    /// <param name="scope"></param>
    /// <param name="request"></param>
    /// <returns></returns>
    public IActivationExpressionResult GetActivationExpression(IInjectionScope scope, IActivationExpressionRequest request)
    {
      var result = request.Services.ExpressionBuilder.DecorateExportStrategy(scope, request, this);

      return result ?? CreateExpression(scope, request, Lifestyle);
    }

    /// <summary>Add a secondary strategy for this export strategy</summary>
    /// <param name="secondaryStrategy">new secondary strategy</param>
    public void AddSecondaryStrategy(ICompiledExportStrategy secondaryStrategy)
    {
      if (null == secondaryStrategy) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.secondaryStrategy);

      _secondaryStrategies = _secondaryStrategies.Add(secondaryStrategy);
    }

    /// <summary>Provide secondary strategies such as exporting property or method</summary>
    /// <returns>export strategies</returns>
    public IEnumerable<ICompiledExportStrategy> SecondaryStrategies() => _secondaryStrategies;

    /// <summary>Get an activation strategy for this delegate</summary>
    /// <param name="scope">injection scope</param>
    /// <param name="compiler"></param>
    /// <param name="activationType">activation type</param>
    /// <returns>activation delegate</returns>
    public virtual ActivationStrategyDelegate GetActivationStrategyDelegate(
      IInjectionScope scope, IActivationStrategyCompiler compiler, Type activationType)
    {
      if (StrategyDelegate != null) { return StrategyDelegate; }

      var request = compiler.CreateNewRequest(activationType, 1, scope);

      var expression = GetActivationExpression(scope, request);

      var newDelegate = compiler.CompileDelegate(scope, expression);

      Interlocked.CompareExchange(ref StrategyDelegate, newDelegate, null);

      return StrategyDelegate;
    }

    /// <summary>Get an activation expression for this strategy</summary>
    /// <param name="scope"></param>
    /// <param name="request"></param>
    /// <param name="lifestyle"></param>
    /// <returns></returns>
    public IActivationExpressionResult GetDecoratorActivationExpression(
      IInjectionScope scope, IActivationExpressionRequest request, ICompiledLifestyle lifestyle)
        => CreateExpression(scope, request, lifestyle);

    /// <summary>Type of activation strategy</summary>
    public override ActivationStrategyType StrategyType { get; } = ActivationStrategyType.ExportStrategy;

    /// <summary>Allow for null returns</summary>
    public bool? AllowNullReturn { get; set; }

    /// <summary>Create expression that is implemented in child class</summary>
    /// <param name="scope"></param>
    /// <param name="request"></param>
    /// <param name="lifestyle"></param>
    /// <returns></returns>
    protected abstract IActivationExpressionResult CreateExpression(
      IInjectionScope scope, IActivationExpressionRequest request, ICompiledLifestyle lifestyle);
  }
}